library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
use work.alu_type.all;

entity arithmetic_logic_unit is
generic (	N : integer := 32;
		M : integer := 5);
port(	left			: in   std_logic_vector (N-1 downto 0);	-- left operand (A or NPC)
		right			: in   std_logic_vector (N-1 downto 0);	-- right operand (B or Imm)
		alu_opcode	: in   TYPE_OP;	-- operation to perform (activates the correct output)
		output		: out std_logic_vector (N-1 downto 0);	-- ALU output
		status		: out std_logic								-- unused (should set the OF flag)
);
end arithmetic_logic_unit;

architecture Behavioral of arithmetic_logic_unit is
component adder_evo is
generic (	N : integer := 16;
		M : integer := 4);
port(	A	: in   std_logic_vector (N-1 downto 0);
		B	: in   std_logic_vector (N-1 downto 0);
		C_in	: in   std_logic;
		SUB	: in   std_logic;
		SIGN: in   std_logic;
		OVF	: out std_logic;
		S 	: out std_logic_vector (N-1 downto 0)
);
end component;

component logicals is
generic (N:integer:=32);
port (	R1	: in std_logic_vector (N-1 downto 0);
		R2	: in std_logic_vector (N-1 downto 0);
		S	: in std_logic_vector (3 downto 0);
		L	: out std_logic_vector (N-1 downto 0)
	);
end component;

component shifter is
generic (	N 		: natural := 32;
		N_SHAMT	: natural := 5 );
port (	ARITH	: in   STD_LOGIC;			-- '0' means logIcal shift, '1' means arithmetic shift
		DIR		: in   STD_LOGIC;			-- '0' means left shift/rotate, '1' means right shift/rotate
		D_IN	: in   STD_LOGIC_VECTOR (N-1 downto 0);
		SHAMT	: in   STD_LOGIC_VECTOR (N_SHAMT-1 downto 0);
		D_OUT	: out STD_LOGIC_VECTOR (N-1 downto 0) 
);
end component;

component comparator is
generic (N:integer:=32);
port (	subtraction	: in   std_logic_vector (N-1 downto 0);
		cmp_need	: in   std_logic_vector (2 downto 0);
		O			: out std_logic_vector (N-1 downto 0)
	);
end component;

signal status_addsub	: std_logic;

signal left_op			: std_logic_vector (N-1 downto 0);
signal right_op			: std_logic_vector (N-1 downto 0);

signal adder_out 		: std_logic_vector (N-1 downto 0);
signal logical_out		: std_logic_vector (N-1 downto 0);
signal shift_out		: std_logic_vector (N-1 downto 0);
signal cmp_out			: std_logic_vector (N-1 downto 0);

signal info 			: std_logic;	-- signed/unsigned, positive/negative, left/right
signal types			: std_logic;	-- addition/subtraction, logical/arithmetical
signal logical_operation	:std_logic_vector (3 downto 0);	-- type of logics operation
signal cmp_want		:std_logic_vector (2 downto 0);	-- type of comparsion selected

begin

process (left, right, alu_opcode, left_op, right_op, status_addsub, adder_out, logical_out, shift_out, cmp_out)
begin
	
	status 	<= '0';
	info 		<= '0';
	types	<= '0';
	cmp_want <= "000";
	logical_operation <= (others => '0');
	
	left_op	<= left;
	right_op	<= right;
	
	case alu_opcode is
		when ADDS => 					-- addition signed
			info		<= '1';	--signed
			types	<= '0';	--addition
			output	<= adder_out;
			status	<= status_addsub;
		when ADDU => 					-- addition unsigned
			info		<= '0';	--unsigned
			types	<= '0';	--addition
			output	<= adder_out;
			status	<= status_addsub;
		when SUBS => 					-- subtraction signed
			info		<= '1';	--signed
			types	<= '1';	--subtraction
			output	<= adder_out;
			status	<= status_addsub;
		when SUBU => 					-- subtraction unsigned
			info		<= '0';	--unsigned
			types	<= '1';	--subtraction
			output	<= adder_out;
			status	<= status_addsub;
		when BIT_AND =>
			logical_operation <= "1000";
			output <= logical_out;
		when BIT_OR => 
			logical_operation <= "1110";
			output <= logical_out;
		when BIT_XOR => 
			logical_operation <= "0110";
			output <= logical_out;
		when LLSH => 					
			info 		<= '0';			--logical
			types 	<= '0';			--left
			output 	<= shift_out;
		when LRSH => 					
			info 		<= '0';			--logical
			types 	<= '1';			--right
			output 	<= shift_out;
		when ALSH => 					
			info 		<= '1';			--arithmetic
			types 	<= '0';			--left
			output 	<= shift_out;
		when ARSH => 					
			info 		<= '1';			--arithmetic
			types 	<= '1';			--right
			output 	<= shift_out;
		when GREATER =>
			info		<= '1';			--signed
			types	<= '1';			--subtraction
			cmp_want <= "100";		-- >
			output	<= cmp_out;
		when LESS=>
			info		<= '1';			--signed
			types	<= '1';			--subtraction
			cmp_want <= "010";		-- <
			output	<= cmp_out;
		when EQUALS =>
			info		<= '1';			--signed
			types	<= '1';			--subtraction
			cmp_want <= "001";		-- =
			output	<= cmp_out;
		when NOT_EQ =>
			info		<= '1';			--signed
			types	<= '1';			--subtraction
			cmp_want <= "110";		-- !=
			output	<= cmp_out;
		when LE_EQ =>
			info		<= '1';			--signed
			types	<= '1';			--subtraction
			cmp_want <= "011";		-- <=
			output	<= cmp_out;
		when GR_EQ =>
			info		<= '1';			--signed
			types	<= '1';			--subtraction
			cmp_want <= "101";		-- >=
			output	<= cmp_out;
		when LESS_U =>
			info		<= '0';			--unsigned
			types	<= '1';			--subtraction
			cmp_want <= "010";		-- <
			output	<= cmp_out;
		when GREATER_U =>
			info		<= '0';			--unsigned
			types	<= '1';			--subtraction
			cmp_want <= "100";		-- >
			output	<= cmp_out;
		when LE_EQ_U =>
			info		<= '0';			--unsigned
			types	<= '1';			--subtraction
			cmp_want <= "011";		-- <=
			output	<= cmp_out;
		when GR_EQ_U =>
			info		<= '0';			--unsigned
			types	<= '1';			--subtraction
			cmp_want <= "101";		-- >=
			output	<= cmp_out;
		when MOV =>
			output	<= right_op;
		when others =>
			output <= (others => '0');
	end case;

end process;

ADD_SUB	: adder_evo generic map(N,M) port
map (	A		=> left_op,
		B		=> right_op,
		C_in		=> '0',
		SUB		=> types,
		sign		=> info,
		OVF		=> status_addsub,
		S		=> adder_out
	);
	
LOGICS: logicals generic map (N) port 
map (	R1	=> left_op,
		R2	=> right_op,
		S	=> logical_operation,
		L	=> logical_out
	);

SHIFT : shifter generic map(N,M) port
map (	ARITH	=> info,
		DIR		=> types,
		D_IN	=> left_op,
		SHAMT	=> right_op(M-1 downto 0),
		D_OUT	=> shift_out
	);

CMP: comparator generic map (N) port 
map (	subtraction	=> adder_out,
		cmp_need	=> cmp_want,
		O			=> cmp_out
	);

end Behavioral;
